home *** CD-ROM | disk | FTP | other *** search
- .!****************************************************************************
- .!
- .! ANTIC PUBLISHING INC., COPYRIGHT 1985. REPRINTED BY PERMISSION.
- .!
- .! ** Professional GEM ** by Tim Oren
- .!
- .! Proff File by ST enthusiasts at
- .! Case Western Reserve University
- .! Cleveland, Ohio
- .! uucp : decvax!cwruecmp!bammi
- .! csnet: bammi@case
- .! arpa : bammi%case@csnet-relay
- .! compuserve: 71515,155
- .!
- .!****************************************************************************
- .!
- .!
- .!****************************************************************************
- .!
- .! Begin Part XIII
- .!
- .!****************************************************************************
- .!
- .PART XIII A New Form Manager
- .PP
- This is the 13th installment of ST PRO GEM, and the first
- devoted to explaining a large piece of code. This article is also
- the second in a series of three concerning GEM user interface
- techniques. The code is an alternate form (dialog) manager for
- GEM. It is stored as GMCL13.C in DL3 of PCS-58. You should go
- and download it now, or you will have no hope of following this
- discussion.
- .PP
- What is unique about this version of the form manager?
- First, it implements all of the functions of the standard GEM
- form_do routine, as well as adding a "hot spots" feature which
- causes selectable objects to become mouse-sensitive, just like
- the entries in menu dropdowns. The second (and obvious)
- difference is that this form manager is provided in source code
- form. This gives you the freedom to examine it and change it to
- suit your own needs.
- .PP
- I have several purposes in presenting this code. It is
- intended as an example of GEM program structure, and an
- application of some of the techniques presented in earlier
- columns. It is also relevant to the continuing thread discussing
- the necessity of feedback when constructing a user interface.
- .PP
- Also, this issue represents the beginning of a fundamental
- change in direction for ST PRO GEM. Since this column began last
- August, Atari ST developers have increased not only in number, but
- in sophistication. A number of books, as well as back issues of
- ST PRO GEM, are now available to explain the basics of the ST and
- GEM. Quick answers to common questions are available here on
- Compuserve's PCS-57 from Atari itself, or from helpful members of
- the developer community.
- .PP
- To reflect these changes, future columns will discuss more
- advanced topics in greater depth, with an accent on code which can
- be adapted by developers. The program presented in this issue
- will be a basis for a number of these discussions. There will be
- fewer "encyclopediac" treatments of AES and VDI function calls.
- Finally, to give me the time required to create this code or clean
- up tools from my "bag of tricks", ST PRO GEM will probably convert
- to a monthly format around the start of summer.
- .SH ON WITH THE SHOW
- Taking your listing in hand, you will
- quickly notice two things. First, this program uses the infamous
- portability macros, so that it may be used with Intel versions of
- GEM. Second, the routines are arranged "bottom up", with the main
- at the end, and subroutines going toward the beginning. (This is
- a carry-over from my days with ALGOL and PASCAL.) You should now
- turn to the form_do entry point near the end of the code.
- .PP
- One change has been made in the standard calling sequence for
- m_do. The starting edit field is now a pointer to a value,
- rather than the value itself. The new form_do overwrites the
- initial value with the number of the object being edited when the
- dialog terminated. Using this information, your program can
- restore the situation when the dialog is next called. As before,
- if there is NO editable field, the initial value should be zero.
- .PP
- There are several local variables which maintain vital state
- information during the dialog interaction. Edit_obj is the number
- of the editable object currently receiving keystrokes. Next_obj
- is set when the mouse is clicked over an object. If the object
- happens to be editable, next_obj becomes the new edit_obj.
- .PP
- Three variables are associated with the "hot-spot" feature.
- If hot_mode is set to M1_ENTER, then the mouse is outside the area
- of the dialog. If it equals M1_EXIT, then the mouse is currently
- in the dialog. If it is in the dialog, hot_obj indicates whether
- there is an active "hot" object. If its value is NIL (-1), then
- there is no active object. Otherwise, it is equal to the number
- of the object which is currently "hot", that is, inverted on the
- screen. Finally, hot_rect is the current wait rectangle. If the
- mouse is outside of the window, then the wait rectangle equals the
- dialog's ROOT. If there is a current hot object, then hot_rect
- equals that object's screen rectangle. If the mouse is in the
- dialog, but not within a hot object, then the wait rectangle
- defines the area within which no further collision checks are
- necessary. This is arrived at through an algorithm explained
- below.
- .PP
- Form_do's initialization code sets up the hot-spot variables
- to trigger if the mouse is within the dialog. It also sets
- starting values for edit_obj and next_obj which will cause the
- edit startup code to be activated.
- .PP
- The main portion of form_do is a loop, exhibiting the type of
- event driven structure discussed in the last column. Before
- entering the evnt_multi wait, the status of next_obj and edit_obj
- are checked to see if a new object should be initialized for
- editing. If so, objc_edit is called with the EDINIT function
- code. NOTE: the objc_edit calling sequence used in this program
- differs from the one given in the AES manual, which is incorrect!
- You should check the bindings you are using to be sure they will
- work with this code, and modify as necessary.
- .PP
- The evnt_multi is set up to wait for a mouse click (single or
- double), for a keyboard input, or for the mouse to make a
- "significant" movement, as discussed above. Notice that since
- form_do is used as a subroutine, it does not handle messages which
- are normally processed by the main loop of your application.
- Notice that this creates a mode, and that this routine as written
- handles modal dialogs. You could, however, use this code as the
- basis for a non-modal dialog handler by drawing the dialog within
- a window, and combining the main loop of form_do with the main
- loop of your application. (This possibility may be examined in
- future columns. In the meantime, it is left as an exercise for
- the reader.)
- .PP
- The event bit vector is returned to the variable "which".
- Since events are not mutually exclusive, each possible event type
- must be examined in turn before returning to the evnt_multi call.
- The form manager's event handling routines are form_hot, for mouse
- rectangle event, form_keybd, for character input, and form_button,
- for mouse clicks. Form_keybd and form_button are allowed to
- terminate the dialog by returning a value of false to the loop
- control variable "cont". If termination is imminent, or the user
- has clicked on a new editable object, objc_edit is called with
- EDEND to remove the cursor from the old object. The normal flow
- of control then returns to edit setup and evnt_multi.
- .PP
- A few cleanup actions are performed upon termination. If the
- terminating object (stored in next_obj) is not the same as the
- hot_obj, then a race condition has occured and the hot object must
- be cleared with objc_toggle before exiting. After this test, the
- final edit_obj value is passed back via the parameter, and the
- terminating object is returned as the function value.
- .SH RELAXEN UND WATCHEN DAS BLINKENLICHTE
- Form_hot is
- responsible for maintaining on-screen hot-spots, and correctly
- updating the internal hot-spot variables. It is about halfway
- through the listing.
- .PP
- he first action in form_hot is to determine if the mouse has
- just exited an object which is "hot. In this case, objc_toggle is
- called to unhighlight the object and reset the SELECTED flag.
- .PP
- The current mouse position is passed to form_hot by form_do.
- It is checked against the root rectangle of the dialog to see if
- the mouse is inside the dialog. If not, the program must wait for
- it to re-enter, so form_hot sets the rectangle and waiting mode
- accordingly, and returns NIL as the new hot_obj.
- .PP
- When the mouse is within the dialog, a regular objc_find call
- determines the object at which it is pointing. For an object to
- be mouse-sensitive, it must be SELECTABLE and not DISABLED. If
- the found object meets these tests, the mouse will "hover" over
- the object, waiting to leave its screen rectangle. Since the
- object might already be SELECTED (and hence drawn reversed), this
- is checked before objc_toggle is called to do the highlighting and
- selection of the object, which becomes the hot_obj. (If the
- object was already SELECTED, the hot_obj becomes NIL.)
- .PP
- The toughest condition is when the mouse is within the
- dialog, but not over a mouse-sensitive object. The regular GEM
- event structure will not work, because it can only wait on two
- rectangles, and there may be many more selectable objects in a
- dialog tree. I have found a way around this limitation using a
- combination of the map_tree utility (introduced in ST PRO GEM #5)
- with the principle of visual hierarchy in object trees.
- .PP
- In summary, the algorithm attempts to find the largest
- bounding rectangle around the current mouse position, within which
- there are no mouse-sensitive objects. It starts with a rectangle
- equal to the dialog root, and successively "breaks" it with the
- rectangle of each mouse-sensitive object. The next few paragraphs
- examine this method in detail.
- .PP
- Since C lacks the dynamic scoping of LISP, from which
- map_tree was derived, it is necessary to set up some globals to be
- used during the rectangle break process. Br_rect is the GRECT of
- the current bounding rectangle. Br_mx and br_my hold the current
- mouse position. Br_togl is a switch which determines whether the
- next break will be attempted horizontally or vertically. After
- initializing these variables, form_hot uses map_tree to invoke the
- break_obj routine for every object in the dialog.
- .PP
- Break_obj first intersects the rectangle of the object with
- the current bounding rectangle. If they are disjoint, then
- neither the object nor any of its offspring can possible affect
- the operation, so FALSE is returned, causing map_tree to ignore
- the subtree.
- .PP
- The object is next checked to see if it is mouse-sensitive.
- As before, it must be SELECTABLE and not DISABLED, and it must not
- be hidden (this was checked automatically by objc_find before).
- If these conditions are met, then the object intrudes into the
- current bounding rectangle. To maintain the desired condition,
- part of the rectangle must be removed or "broken away".
- .PP
- In many cases, the break operation can be done either
- horizontally or vertically. Since we have no prior information as
- to which way the mouse will move next, break_obj uses the br_togl
- flag to alternate which direction it will try first. This should
- yield the most nearly square rectangle.
- .PP
- The break_x and break_y routines are very similar. In each
- case, the segment occupied by the breaking object is compared to
- the point occupied by the mouse. If the point is within the
- segment, there is no possible break in this dimension, and FALSE
- is returned. If the point lies outside the segment, then the
- rectangle may be successfully broken by reducing this dimension.
- This is done, and TRUE is returned to report success.
- .PP
- The break_y routine also employs a look-ahead test to prevent
- a possible infinite loop. It is conceivable, though not likely,
- that someone might nest a non-SELECTABLE object completely within
- another SELECTABLE object(s). If the mouse point is within such
- an object, the algorithm will not be able to select a break
- dimension. In the current version, the mouse rectangle is simply
- forced to a single pixel for this case. (Note that is is NOT
- sufficent to simply wait on the non-selectable object's rectangle,
- since other SELECTABLE objects may overlap it and follow it in
- tree order.)
- .PP
- Since map_tree examines all possible objects, br_rect will be
- the correct bounding rectangle at completion. Note that you can
- readily adapt this technique to other uses, such as hot-spotting
- while dragging objects. It is much less demanding of CPU
- resources than other methods, such as repetitive objc_finds.
- .SH WHAT A CHARACTER!
- The form_keybd routine acts as a filter on
- character input. When it recognizes a control character, it
- processes it and zeroes the keyboard word. Other chararacters are
- passed on to objc_edit to be inserted in edit_obj. If there is no
- editing object, the character goes to the bit bucket.
- .PP
- The form_keybd given implements the standard GEM
- functionality with two minor additions. First, a carriage return
- in a dialog with no DEFAULT exit object is taken as a tab. This
- allows <CR> to be used "naturally" in dialogs with several lines
- of text input. Second, tabs and backtabs "wrap around" from top
- to bottom of the dialog, and are done by "walking the tree",
- rather than relying on the LASTOB flag to signal the end of the
- dialog. This allows the new form manager to handle dialog trees
- which are not contiguous in memory.
- .PP
- The code sets up several global variables for use by mapped
- functions. Fn_obj is the output from both find_tab and find_def.
- Fn_dir is an input to find_tab. Fn_last, fn_prev, and fn_last are
- used while searching for tab characters.
- .PP
- A carriage return results in a search of the entire tree,
- using map_tree and find_def, for an object with its DEFAULT flag
- set. Its SELECTED flag is set and it is inverted on the screen to
- indicate the action taken. Form_keybd returns a FALSE to force
- termination of the main form_do loop. If no DEFAULT is found,
- control passes to the tab code.
- .PP
- The tabbing procedure is somewhat complicated because the
- same code is used for forward and backward tabbing. The old value
- of edit_obj (the object being tabbed FROM) is placed into fn_last.
- Fn_dir is set to one for a forward tab, and zero for a backward
- tab.
- .PP
- The general strategy is to scan the entire tree for EDITABLE
- objects, always saving the last one found in fn_prev. When
- tabbing forward fn_last is checked against fn_prev. A match
- indicates that the current object is the one desired. When
- tabbing backward the current object is checked against fn_last.
- If they match, fn_prev is the desired object. This procedure
- requires two passes when the tab "wraps around" the tree, that is,
- when the desired object as at the opposite end of the traverse
- from the old editing object.
- .PP
- The result of the tab operation is written back into
- form_do's next_obj parameter, and becomes the new editing object
- at the beginning of the next loop.
- .SH BUTTON DOWN
- The form_button procedure is lengthy because it
- must recognize and handle mouse clicks on several types of
- objects: EDITABLE, SELECTABLE, and TOUCHEXIT. The first section
- of code rejects other objects, which cannot accept a click.
- .PP
- The next piece of form_button makes a special check for a
- double click on a TOUCHEXIT object. This will cause the high bit
- of the returned object number to be set. (By the way, this also
- occurs in the standard form_do.) This flag allows user dialog
- code to perform special processing on the object.
- .PP
- The largest piece of form_button handles the various cases in
- which the SELECTABLE flag may be set. Setting the RBUTTON (radio
- button) flag causes all of the object's siblings in the tree to be
- deselected at the time the object is clicked. The do_radio
- routine uses the get_parent utility to find the ancestor, and then
- performs the deselect/select operation.
- .PP
- If the SELECTABLE object is not TOUCHEXIT, then graf_watchbox
- is used to make sure that the mouse button comes back up while it
- is within the object. Otherwise, the click is cancelled. Care is
- necessary here, since the hot-spot code may have already set the
- SELECTED flag for the object. (We cannot be sure of this, for a
- race condition may have occurred!)
- .PP
- If the SELECTABLE object is TOUCHEXIT, then the application
- has requested that form_do exit without waiting for the button to
- go back up. In both this and regular form_do, TOUCHEXIT objects
- are used when you want to provide immediate response (animation)
- within the context of a dialog.
- .PP
- The final parts of form_button do cleanup. If the clicked
- object was already hot-spotted, hot_obj must be reset to NIL,
- otherwise form_do will carefully unselect the object which has
- just been selected!
- .PP
- If the EXIT or TOUCHEXIT flags are in force, form_button
- returns FALSE to force the completion of form_do. For EDITABLE
- objects, next_obj is left intact to replace edit_obj during the
- next loop. Otherwise, next_obj has done its job and is zeroed,
- and form_button returns TRUE for continuation.
- .PP
- This concludes the tour of the alternate form_do. The best
- cure for any confusion in this explanation is to compile the code
- into an application and watch how it runs with different
- resources, or attack it with a debugger.
- .SH OPERATORS ARE STANDING BY
- I encourage you to modify this
- code to meet your particular needs and incorporate it into your
- application. I would like to request than anyone who comes up
- with significant improvements (or bug fixes) send them to me so
- they can be made generally available. You can do this via the
- ANTIC ONLINE Feedback, or by sending E-mail to 76703,202.
- .PP
- Speaking of Feedback, I would also like comments on the
- proposed change of direction for the column, and more suggestions
- for future topics. The next installment will be a further
- discussion of interface design. Topics now queued for future
- articles include the file selector and DOS error handling, a new
- object editor, and customized drag box and rubber box routines.
- Discussions on VDI workstations and printer output are on hold
- pending release of the GDOS by Atari. If there are items which
- you want to appear here, you must let me know!
- .!
- .!
- .!*****************************************************************************
- .!* *
- .!* End Part XIII *
- .!* *
- .!*****************************************************************************
-